package com.effects;

import com.Utils;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;

import ddf.minim.analysis.FFT;

/**
 * @author sad
 */
public abstract class BasicEffect {

    private static final float FREQUENCY = 44100;
    private static final int SPECTRUM_LENGTH = 78;
    private static final BufferedImage defImage = new BufferedImage(SPECTRUM_LENGTH, 39, BufferedImage.TYPE_3BYTE_BGR);
    protected static Color[] rainbow;
//    private final float[] array = new float[4096];
//    private final double[] array2 = new double[2048];
    protected final BufferedImage image = new BufferedImage(SPECTRUM_LENGTH, 39, BufferedImage.TYPE_INT_RGB);
    protected final double[] spectrum = new double[SPECTRUM_LENGTH];
    private final int[] tempPixelBuffer = new int[3];

    static {
        rainbow = generateColors(78);
    }

    public int nextPowerOfTwo(int val) {
        return 1 << (32 - Integer.numberOfLeadingZeros(val - 1));
    }

    protected double[] spectrum(byte[] pcm) {

        int size = nextPowerOfTwo(pcm.length / 2)/2;

        float [] array = new float[size];

        for (int i = 0,j=0; i < size; i++,j+=2) {
            int value = Utils.lhTo16Bit(pcm[j], pcm[j+1]);
            array[i] = (float) value;
        }


        FFT fft = new FFT( array.length, FREQUENCY);
        fft.forward( array);

        int offset= 3;
        float step = size / (float)(spectrum.length-offset);

        double min = 9999999999f;
        double max = 0;
        for (int i = 0; i < spectrum.length; i++) {

            spectrum[i] = Math.sqrt(fft.getFreq(step*(i+offset)));
            if (spectrum[i] < min) {
                min = spectrum[i];
            }

            if (spectrum[i] > max) {
                max = spectrum[i];
            }
        }

        double del = max;


        for (int i = 0; i < spectrum.length; i++) {
            spectrum[i]=(spectrum[i]-min)/del;
//            System.out.println(max);
        }
        return spectrum;
    }

    protected void alphaClearingImage(BufferedImage image, int level) {
        WritableRaster raster = image.getRaster();
        for (int j = 0; j < image.getHeight(); j++) {
            for (int i = 0; i < image.getWidth(); i++) {
                raster.getPixel(i, j, tempPixelBuffer);
                tempPixelBuffer[0] -= level;
                if (tempPixelBuffer[0] < 0) {
                    tempPixelBuffer[0] = 0;
                }

                tempPixelBuffer[1] -= level;
                if (tempPixelBuffer[1] < 0) {
                    tempPixelBuffer[1] = 0;
                }

                tempPixelBuffer[2] -= level;
                if (tempPixelBuffer[2] < 0) {
                    tempPixelBuffer[2] = 0;
                }

                raster.setPixel(i, j, tempPixelBuffer);
            }
        }
    }

    public static Color[] generateColors(int n) {
        Color[] cols = new Color[n];
        for (int i = 0; i < n; i++) {
            cols[i] = Color.getHSBColor((float) i / (float) n, 0.85f, 1.0f);
        }

        return cols;
    }

    public abstract String getEffectName();

    public abstract String getId();

    @Override
    public String toString() {
        return getEffectName();
    }

    protected Color getRainbow(int i, int offset) {
        try {
            i += offset;
            if (i < 0) {
                i = rainbow.length + i;
            }

            i = i % rainbow.length;
            return rainbow[i];
        } catch (ArrayIndexOutOfBoundsException ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public BufferedImage getImage(byte[] pcm) {
        return defImage;
    }

    public static BasicEffect[] getAllEffects() {
        return new BasicEffect[]{new LineBarsEffect(), new MovedFrequencyEffect(), new AmplitudeEffect(), new AmplitudeEffect2(), new EqualizerEffect(), new RainbowEffect(), new CirclesEffect(),new DrawLinesEffect(), new PlasmaEffect()};
    }
}